#version 430 core
out vec4 FragColor;

in vec3 v2f_Normal;
in vec3 v2f_FragPosInWorldSpace;

uniform samplerCube u_DepthMap;
uniform vec3 u_LightPos;
uniform vec3 u_ViewPos;
uniform float u_FarPlane;

// array of offset direction for sampling
vec3 GridSamplingDisk[20] = vec3[]
(
   vec3(1, 1,  1), vec3( 1, -1,  1), vec3(-1, -1,  1), vec3(-1, 1,  1), 
   vec3(1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1),
   vec3(1, 1,  0), vec3( 1, -1,  0), vec3(-1, -1,  0), vec3(-1, 1,  0),
   vec3(1, 0,  1), vec3(-1,  0,  1), vec3( 1,  0, -1), vec3(-1, 0, -1),
   vec3(0, 1,  1), vec3( 0, -1,  1), vec3( 0, -1, -1), vec3( 0, 1, -1)
);

float ShadowCalculation(vec3 vFragPos)
{
    // get vector between fragment position and light position
    vec3 FragToLight = vFragPos - u_LightPos;
    
    // now get current linear depth as the length between the fragment and light position
    float CurrentDepth = length(FragToLight);

    float Shadow = 0.0;
    float Bias = 0.15;
    int Samples = 20;
    float ViewDistance = length(u_ViewPos - vFragPos);
    float DiskRadius = (1.0 + (ViewDistance / u_FarPlane)) / 25.0;
    for(int i = 0; i < Samples; ++i)
    {
        float ClosestDepth = texture(u_DepthMap, FragToLight + GridSamplingDisk[i] * DiskRadius).r;
        ClosestDepth *= u_FarPlane;   // undo mapping [0;1]
        if(CurrentDepth - Bias > ClosestDepth)
            Shadow += 1.0;
    }
    Shadow /= float(Samples);
    
    return Shadow;
}

void main()
{
	vec3 Color = vec3(1.0f);
	vec3 Normal = normalize(v2f_Normal);

	vec3 LightColor = vec3(0.3f);

	// Ambient
	vec3 Ambient = 0.3f * Color;
	// Diffuse
	vec3 LightDir = normalize(u_LightPos - v2f_FragPosInWorldSpace);
	float Diff = max(dot(LightDir, Normal), 0);
	vec3 Diffuse = Diff * LightColor;

	float Shadow = ShadowCalculation(v2f_FragPosInWorldSpace);
	vec3 Lighting = (Ambient + (1.0f - Shadow) * Diffuse) * Color;

	FragColor = vec4(Lighting, 1.0f);
}